package com.dant.managers
{
	import com.dant.interfaces.IDragDropManager;
	import com.dant.utils.SnapshotUtil;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import mx.containers.Accordion;
	import mx.containers.Box;
	import mx.containers.BoxDirection;
	import mx.containers.TabNavigator;
	import mx.controls.Image;
	import mx.controls.scrollClasses.ScrollThumb;
	import mx.core.Container;
	import mx.core.DragSource;
	import mx.core.UIComponent;
	import mx.events.DragEvent;
	import mx.managers.DragManager;
	import mx.utils.UIDUtil;
	
	public class DragDropManager extends EventDispatcher implements IDragDropManager
	{
		public static const DEFAULT_DRAG_FORMAT:String = "object";
		
        private var currentIndex:int;
        private var isDragging:Boolean;
        private var _dragFormat:String = DEFAULT_DRAG_FORMAT;
        private var xPositionMap:Object;
        private var xClickedOnDrag:Number;
        private var yPositionMap:Object;
        private var yClickedOnDrag:Number;
        private var registeredDraggableObjectsMap:Object = {};
        private var registeredDroppableObjectsMap:Object = {};

        public function set dragFormat (value:String):void
        {
        	_dragFormat = value;
        }

		public function get dragFormat():String
		{
			return (_dragFormat);
		}

		public function DragDropManager(target:IEventDispatcher=null)
		{
			super(target);
		}

        protected function registerObjectToDragMap ( object:UIComponent ):void
        {
        	registeredDraggableObjectsMap [ UIDUtil.getUID(object) ] = object;
        }

        protected function registerObjectToDropMap ( object:UIComponent ):void
        {
        	registeredDroppableObjectsMap [ UIDUtil.getUID(object) ] = object;
        }

        public function registerObjectToDragAndDrop ( object:UIComponent ):UIComponent
        {
            if ( !objectIsDraggable(object) )
            {
	            object.addEventListener( MouseEvent.MOUSE_DOWN, object_mouseDownHandler,false,0,true );
	            registerObjectToDragMap ( object );
	        }
            return ( object );
        }

        public function registerAcceptableDropTarget ( object:UIComponent ):UIComponent
        {
            object.addEventListener( DragEvent.DRAG_ENTER, object_dragEnterHandler, false, 1, true );
            object.addEventListener( DragEvent.DRAG_DROP, object_dragDropComponentHandler, false, 1, true );
            
            registerObjectToDropMap(object);
//          object.addEventListener( DragEvent.DRAG_COMPLETE, dragCompleteHandler, false, 1, true );
			return(object);
        }

        public function objectIsDraggable ( object:UIComponent ):Boolean
        {
            return ( registeredDraggableObjectsMap [ UIDUtil.getUID(object) ] != null );
        }

        public function objectIsDropTarget ( object:UIComponent ):Boolean
        {
            return ( registeredDroppableObjectsMap [ UIDUtil.getUID(object) ] != null );
        }

        private function unregisterObjectFromDragMap ( object:UIComponent ):void
        {
            delete (registeredDraggableObjectsMap [ UIDUtil.getUID(object) ]);
        }

        private function unregisterObjectFromDropMap ( object:UIComponent ):void
        {
            delete (registeredDroppableObjectsMap [ UIDUtil.getUID(object) ]);
        }

        public function unregisterObjectToDragAndDrop ( object:UIComponent ):UIComponent
        {
        	if ( objectIsDraggable( object ) )
        	{
	            object.removeEventListener( MouseEvent.MOUSE_DOWN, object_mouseDownHandler );
	            unregisterObjectFromDragMap( object );
            }
            return ( object );
        }

        public function unregisterAcceptableDropTarget ( object:UIComponent ):UIComponent
        {
            object.removeEventListener( DragEvent.DRAG_ENTER, object_dragEnterHandler );
            object.removeEventListener( DragEvent.DRAG_DROP, object_dragDropComponentHandler );
            
            unregisterObjectFromDropMap(object);
            return ( object );
        }

        protected function buildPositionMap ( map:Object, property:String, 
        									container:DisplayObjectContainer ):Object
        {
            if ( ! map )
            {
                map = {};
            }
            
            for ( var i:uint = 0; i < container.numChildren; i++ )
            {
                var object:DisplayObject = container.getChildAt(i);
                var point:Point = object.localToGlobal( new Point ( object.x, object.y ) );
                var position:Number = point[property];
                for ( var j:int = position-5; j<position+5; j++ )
                {
                    map [ j ] = container.getChildAt(i);
                }
            }
            
            return ( map );
        }

		protected function startObjectDrag(object:UIComponent, mouseEvent:MouseEvent):void
		{
			var dragEvent:DragEvent = new DragEvent (DragEvent.DRAG_START);
            dragEvent.dragInitiator = object;

            dragEvent.localX = mouseEvent.localX;
            dragEvent.localY = mouseEvent.localY;

            xClickedOnDrag = mouseEvent.localX;
            yClickedOnDrag = mouseEvent.localY; 

            dragEvent.buttonDown = true;
            object.dispatchEvent(dragEvent);

            var ds:DragSource = new DragSource ( );
            ds.addData(object, dragFormat);

			DragManager.showFeedback( DragManager.MOVE );
			DragManager.doDrag ( object, ds, mouseEvent, takeSnapshot(object) );

			xPositionMap = buildPositionMap(xPositionMap,"x",object.parent);
			yPositionMap = buildPositionMap(yPositionMap,"y",object.parent);
		}

        protected function searchIndexToDropIn ( box:Box, droppedPosition: Number ):Number
        {
            // TODO: Debuggear este método y mejorarlo - DAN
            var currentPosition:Number = 0;
            var componentGap:Number = 0;

            if ( box.direction == BoxDirection.HORIZONTAL )
            {
                componentGap = !(isNaN(box.getStyle("horizontalGap"))) ? 
                    box.getStyle("horizontalGap"):0;
            }
            else
            {
                componentGap = !(isNaN(box.getStyle("verticalGap"))) ? 
                    box.getStyle("verticalGap"):0;
            }

            for ( var i:int = 0; i < box.numChildren; i++ )
            {
                if (droppedPosition < currentPosition)
                {
                    return ( i );
                }
                currentPosition = currentPosition + box.getChildAt(i).width + componentGap;
            }
            return ( box.numChildren-1 );
        }        


		public function takeSnapshot(object:DisplayObject):Image
		{
			var snapshot:Image = SnapshotUtil.takeSnapshot(object);
			return (snapshot);
		}

		//--------------------------------------
		//  Drag n' drop
		//--------------------------------------
		protected function object_mouseDownHandler(event:MouseEvent):void 
		{
			if ( event.isDefaultPrevented() )
			{
				return;
			}

			event.preventDefault();
			var object:UIComponent = UIComponent(event.currentTarget);

			// if the mouseDown is not done on the ScrollBar
			if ( ! (event.target is ScrollThumb) )
			{
				// a mouseMove handler is added to the object
				object.addEventListener(MouseEvent.MOUSE_MOVE, object_dragMoveHandler, false,0,true);
				isDragging = true;
			}
		}

		/**
		 * Drag move event handler
		 */
		protected function object_dragMoveHandler(event:MouseEvent):void 
		{
			var object:UIComponent = UIComponent (event.currentTarget);
			// Bounds of the drag. The panel can not surpass the width of its parent
 			if (object.width < object.parent.width)
			{
				// The current index of the panel is stored. 
				// It will be added to the parent in the same index once is dropped
				currentIndex = object.parent.getChildIndex(object);
//				object.parent.setChildIndex(object, object.parent.numChildren - 1);

				if ( ! DragManager.isDragging ) 
				{
					startObjectDrag(object, event);
				}
				object.removeEventListener(MouseEvent.MOUSE_MOVE, object_dragMoveHandler);
			} 
		}

		protected function object_draggingMoveHandler ( event:MouseEvent ):void
		{
			var object:UIComponent = UIComponent ( event.target );
			var point:Point = object.localToGlobal( new Point ( event.localX, event.localY ) )
			if ( xPositionMap && xPositionMap[point.x] )
			{
				object.graphics.clear();
				object.graphics.beginFill(0xFF66FF,1);
				object.graphics.lineStyle(3,0xFF66FF,1);
				object.graphics.moveTo(event.localX,0);
				object.graphics.lineTo(event.localX,1000);
			} 
		}

		protected function object_dragEnterHandler ( event:DragEvent ):void
		{
			if ( event.dragSource.hasFormat( dragFormat ) )
			{
				if ( (event.target is TabNavigator) || (event.target is Accordion) )
				{
					var ds:DragSource = event.dragSource;
					
					// the object being dragged
					var object:Object = ds.dataForFormat(dragFormat);

					if ( ! (object is Container) )
					{
						return;
					}
				}
				DragManager.acceptDragDrop( event.target as UIComponent );
			}
		}

		protected function object_dragDropComponentHandler ( event:DragEvent ):void
		{
			var ds:DragSource = event.dragSource;
			
			if ( ds.hasFormat(dragFormat) )
			{
				// the object being dragged
				var object:UIComponent = UIComponent(ds.dataForFormat(dragFormat));
				
				// the object in which the object is being dropped.
				var objectParent:Container = Container(event.currentTarget);

/* 				// TabNavigator doesn't accept components that aren't containers as a child
				if ( objectParent is TabNavigator && ( ! object is Container ) )
				{
					return;
				} */
				
				/* In case the drop of the image proxy is made on top of the 
				   component being dragged.
				*/
				if (objectParent == object)
				{
					event.localX = event.localX + object.x;
					event.localY = event.localY + object.y;
				}
				/* In case the component is dropped in a container and have to change
				   parents.
				   We must validate it's no being dropped on its own parent, or any of its children 
				*/
				else if (UIDUtil.getUID(objectParent) != UIDUtil.getUID(object.parent))
				{
					object.parent.removeChild(object);
					objectParent.addChild( object );
				}
				
				/* Calculates the actual position in case the drop targer is a Panel
				or a TitleWindow (because these containers have frames)
				*/
/* 				if (( object.parent is Panel ) || ( object.parent is TitleWindow ))
				{
//					var objectHeader:Object = (object.parent as Panel).getStyle("headerHeight");
//					var objectLeftBorder:Object = (object.parent as Panel).getStyle("borderThicknessLeft");
//					var objectTopBorder:Object = (object.parent as Panel).getStyle("borderThicknessTop");
					var headerHeight:Number = (object.parent as Container).getStyle("headerHeight");
					var leftBorderThickness:Number = (object.parent as Container).getStyle("borderThicknessLeft");
					var topBorderThickness:Number = (object.parent as Container).getStyle("borderThicknessTop");
					
					if ( !isNaN(headerHeight) )
					{
						event.localY = event.localY - headerHeight;
					}
					else
					{
						event.localY = event.localY - DEFAULT_PANEL_HEADER_HEIGHT;
					}
					
					if ( !isNaN(leftBorderThickness)&& leftBorderThickness > 0 )
					{
						event.localX = event.localX - leftBorderThickness;
					}
					
					if ( !isNaN(topBorderThickness)&& topBorderThickness > 0 )
					{
						event.localX = event.localX - topBorderThickness;
					} 
				} */

                var newPosition:Point = UIComponent(object.parent).localToContent( new Point ( event.localX, event.localY ) );
				if ( !(object.parent is Box) )
				{
					/* Sets the new position of the dropped component, subtracting
					   the X and Y where the dragProxy is being dragged.
					   This is made so the drop seems easy on the eye...
					*/
					object.x = newPosition.x - xClickedOnDrag;
					object.y = newPosition.y - yClickedOnDrag;
//					object.parent.setChildIndex(object,0);
					object.invalidateDisplayList();
				}
				else
				{
					// TODO: Mejorar el drop en el BOX para poder hacer un cambio de indice con drag & drop. Calcular la posición en la que debe ir el componente que se soltó. - DAN
					// TODO: Encontrar el error que lanza cuando hace un drop en el box.  "No se puede convertir de algún skin a otra vaina" - DAN
					var box:Box = object.parent as Box;
					var index:int = 0;
					if ( box.direction == BoxDirection.HORIZONTAL )
					{
						index = searchIndexToDropIn(box,newPosition.x);
						box.setChildIndex(object,index);
					} 
					else
					{
						index = searchIndexToDropIn(box,newPosition.y);
						box.setChildIndex(object,index);
					}
				}
				object.alpha = 1;
				xClickedOnDrag = 0;
				yClickedOnDrag = 0;
//				selectComponent(object);

			}
		}
	}
}